package com.thread.threadfirst.studyThread1;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * 18、上面的过于繁琐，下面用Latch（门闩）替代wait、notify来进行等待、通知
 * 好处是通信简单，同时也可以指定等待时间
 * 使用await和countDown方法替代wait和notify
 * CountDownLatch不涉及锁定，即此类的方法不需要锁定对象，当count的值为0时当前线程继续往下运行，而且执行counDown()方法的当前线程也可以继续往下执行
 * 当不涉及同步，只是涉及线程通信的时候，用synchronized + wait/notify就显得太重了
 * 这时应该考虑countdownlatch/cyclicbarrier/semaphore
 */
public class T10 {
    volatile List list = new ArrayList();

    public void add(Object o) {
        list.add(o);
    }

    public int size() {
        return list.size();
    }

    public static void main(String[] args) {
        T10 t10 = new T10();

        //当countdownlatch=0时打开门闩 门就开了
        CountDownLatch latch = new CountDownLatch(1);

        new Thread(() -> {
            System.out.println("t2开始……");
            if (t10.size() != 5) {
                try {
                    //相当于object.wait()，让当前线程等待在门外，但是此方法是不需要锁定对象的
                    latch.await();
                    //也可以指定时间
//                    latch.await(5000,TimeUnit.MILLISECONDS);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("t2结束……");
        }, "t2").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            System.out.println("t1开始……");
            for (int i = 0; i < 10; i++) {
                t10.add(new Object());
                System.out.println("add " + i);

                if (t10.size() == 5) {
                    //countDown()：将countDown的值减1，因为刚开始就是1，减1后等于0，则门闩打开，t2得以执行，此方法也是不需要锁定对象的，
                    //而且打开门闩后，上面之前被门闩挡在门外的t2线程现在可以继续往下执行了，而且当前线程t1打开门闩后也是可以继续往下执行代码的。
                    latch.countDown();
                }
                //这里如果没有sleep()的话，不能保证t2在解除门闩后能立即执行
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "t1").start();

    }
}
